home *** CD-ROM | disk | FTP | other *** search
/ Aminet 50 / Aminet 50 (2002)(GTI - Schatztruhe)[!][Aug 2002].iso / Aminet / text / tex / dvipdfm.lha / dvipdfm / pdfdoc.c < prev    next >
C/C++ Source or Header  |  2000-10-20  |  37KB  |  1,284 lines

  1. /*  $Header$
  2.  
  3.     This is dvipdfm, a DVI to PDF translator.
  4.     Copyright (C) 1998, 1999 by Mark A. Wicks
  5.  
  6.     This program is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation; either version 2 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     This program is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with this program; if not, write to the Free Software
  18.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.     
  20.     The author may be contacted via the e-mail address
  21.  
  22.     mwicks@kettering.edu
  23. */
  24.  
  25.  
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <time.h>
  29. #include "system.h"
  30. #include "config.h"
  31. #include "mem.h"
  32. #include "error.h"
  33. #include "mfileio.h"
  34. #include "numbers.h"
  35. #include "dvi.h"
  36. #include "pdflimits.h"
  37. #include "pdfobj.h"
  38. #include "pdfdev.h"
  39. #include "pdfdoc.h"
  40. #include "pdfspecial.h"
  41.  
  42. #ifdef HAVE_LIBPNG
  43. #include "thumbnail.h"
  44. #endif
  45.  
  46. static pdf_obj *catalog = NULL;
  47. static pdf_obj *docinfo = NULL;
  48. static pdf_obj *page_tree = NULL, *page_tree_ref = NULL;
  49.  
  50. int outline_depth=0;
  51. static struct 
  52. {
  53.   int kid_count;
  54.   pdf_obj *entry;
  55. } outline[MAX_OUTLINE_DEPTH];
  56.  
  57.  
  58. static pdf_obj *current_page_resources = NULL;
  59. static pdf_obj *this_page_contents = NULL;
  60. static pdf_obj *glob_page_bop, *glob_page_eop;
  61. static pdf_obj *coord_xform_stream = NULL, *coord_xform_ref = NULL;
  62. static pdf_obj *this_page_bop = NULL;
  63. static pdf_obj *this_page_beads = NULL;
  64. static pdf_obj *this_page_annots = NULL;
  65. static pdf_obj *this_page_xobjects = NULL, *this_page_fonts = NULL;
  66. static pdf_obj *tmp1;
  67.  
  68. static unsigned long page_count = 0;
  69. static struct pages {
  70.   pdf_obj *page_dict;
  71.   pdf_obj *page_ref;
  72. } *pages = NULL;
  73. static unsigned long max_pages = 0;
  74.  
  75. static void start_page_tree (void);
  76. static void create_catalog (void);
  77. static void start_current_page_resources (void);
  78. static void finish_page_tree(void);
  79. static void start_name_tree(void);
  80. static void finish_dests_tree(void);
  81. static void finish_pending_xobjects(void);
  82. static void start_articles(void);
  83.  
  84. static unsigned char verbose = 0, debug=0;
  85.  
  86. void pdf_doc_set_verbose(void)
  87. {
  88.   if (verbose < 255) 
  89.     verbose += 1;
  90. }
  91.  
  92. void pdf_doc_set_debug(void)
  93. {
  94.   debug = 1;
  95. }
  96.  
  97. static void resize_pages (unsigned long newsize)
  98. {
  99.   unsigned long i;
  100.   if (newsize > max_pages) {
  101.     pages = RENEW (pages, newsize, struct pages);
  102.     for (i=max_pages; i<newsize; i++) {
  103.       pages[i].page_dict = NULL;
  104.       pages[i].page_ref = NULL;
  105.     }
  106.     max_pages = newsize;
  107.   }
  108. }
  109.  
  110. static pdf_obj *type_name, *page_name, *pages_name, *contents_name, *annots_name,
  111.   *resources_name, *bead_name, *count_name, *kids_name, *parent_name,
  112.   *mediabox_name, *limits_name, *thumb_name;
  113.  
  114. static void make_short_cuts(void) 
  115. {
  116. #ifdef MEM_DEBUG
  117. MEM_START
  118. #endif
  119.   /* Define some shorthand for names that will conserve memory (and time)
  120.      (similar to the latex \@ne trick */
  121.   type_name = pdf_new_name("Type");
  122.   page_name = pdf_new_name("Page");
  123.   pages_name = pdf_new_name("Pages");
  124.   count_name = pdf_new_name("Count");
  125.   kids_name = pdf_new_name("Kids");
  126.   parent_name = pdf_new_name("Parent");
  127.   contents_name = pdf_new_name("Contents");
  128.   annots_name = pdf_new_name("Annots");
  129.   resources_name = pdf_new_name ("Resources");
  130.   bead_name = pdf_new_name ("B");
  131.   mediabox_name = pdf_new_name ("MediaBox");
  132.   limits_name = pdf_new_name ("Limits");
  133.   thumb_name = pdf_new_name ("Thumb");
  134. #ifdef MEM_DEBUG
  135. MEM_END
  136. #endif
  137. }
  138. static void release_short_cuts(void)
  139. {
  140.   /* Release those shorthand name we created */
  141.   pdf_release_obj (type_name);
  142.   pdf_release_obj (page_name);
  143.   pdf_release_obj (pages_name);
  144.   pdf_release_obj (count_name);
  145.   pdf_release_obj (kids_name);
  146.   pdf_release_obj (parent_name);
  147.   pdf_release_obj (contents_name);
  148.   pdf_release_obj (annots_name);
  149.   pdf_release_obj (resources_name);
  150.   pdf_release_obj (bead_name);
  151.   pdf_release_obj (mediabox_name);
  152.   pdf_release_obj (limits_name);
  153.   pdf_release_obj (thumb_name);
  154. }
  155.  
  156. static void start_page_tree (void)
  157. {
  158.   if (debug) {
  159.     fprintf (stderr, "(start_page_tree)");
  160.   }
  161.   /* Create empty page tree */
  162.   page_tree = pdf_new_dict();
  163.   page_tree_ref = pdf_ref_obj (page_tree);
  164.   /* Both page_tree and page_tree_ref are kept open until the
  165.      document is closed.  This allows the user to write to page_tree
  166.      if he so choses. */
  167.   /* Link /Pages into the catalog
  168.      and poing it to indirect reference since we
  169.      haven't built the tree yet */
  170.   glob_page_bop = pdf_new_stream(0);
  171.   glob_page_eop = pdf_new_stream(0);
  172.   coord_xform_stream = pdf_new_stream(0);
  173.   coord_xform_ref = pdf_ref_obj (coord_xform_stream);
  174.   return;
  175. }
  176.  
  177. void pdf_doc_bop (char *string, unsigned length)
  178. {
  179.   if (length > 0)
  180.     pdf_add_stream (glob_page_bop, string, length);
  181. }
  182.  
  183. void pdf_doc_this_bop (char *string, unsigned length)
  184. {
  185.   if (this_page_bop == NULL)
  186.     this_page_bop = pdf_new_stream (STREAM_COMPRESS);
  187.   if (length > 0)
  188.     pdf_add_stream (this_page_bop, string, length);
  189. }
  190.  
  191. void pdf_doc_set_origin (double x, double y)
  192. {
  193.   int len;
  194.   static char first = 1;
  195.   if (first) {
  196.     len = sprintf (work_buffer, "%g 0 0 %g %g %g cm\n",
  197.            dvi_tell_mag()*pdf_dev_scale(), dvi_tell_mag()*pdf_dev_scale(),
  198.            x, y);
  199.     pdf_add_stream (coord_xform_stream, work_buffer, len);
  200.     first = 0;
  201.   }
  202. }
  203.  
  204.  
  205. void pdf_doc_eop (char *string, unsigned length)
  206. {
  207.   if (length > 0)
  208.     pdf_add_stream (glob_page_eop, string, length);
  209. }
  210.  
  211. static void start_outline_tree (void)
  212. {
  213.   if (debug) {
  214.     fprintf (stderr, "(start_outline_tree)");
  215.   }
  216.   /* Create empty outline tree */
  217.   outline[outline_depth].entry = pdf_new_dict();
  218.   outline[outline_depth].kid_count = 0;
  219.   return;
  220. }
  221.  
  222. static pdf_obj *names_dict;
  223.  
  224. static void start_name_tree (void)
  225. {
  226.   if (debug) {
  227.     fprintf (stderr, "(start_name_tree)");
  228.   }
  229.   names_dict = pdf_new_dict ();
  230. }
  231.  
  232. static char *asn_date (void)
  233. {
  234. #ifndef HAVE_TIMEZONE
  235.   #ifdef TM_GM_TOFF
  236.      #define timezone (bdtime->gm_toff)
  237.   #else
  238.      #define timezone 0l
  239. #endif /* TM_GM_TOFF */
  240. #endif /* HAVE_TIMEZONE */
  241.   static char date_string[22];
  242.   time_t current_time;
  243.   struct tm *bd_time;
  244.   if (debug) {
  245.     fprintf (stderr, "(asn_date)");
  246.   }
  247.   time(¤t_time);
  248.   bd_time = localtime(¤t_time);
  249.   sprintf (date_string, "D:%04d%02d%02d%02d%02d%02d%0+3ld'%02ld'",
  250.        bd_time -> tm_year+1900, bd_time -> tm_mon+1, bd_time -> tm_mday,
  251.        bd_time -> tm_hour, bd_time -> tm_min, bd_time -> tm_sec,
  252.        -timezone/3600, timezone%3600);
  253.   return date_string;
  254. }
  255.  
  256. #define BANNER "dvipdfm %s, Copyright \251 1998, by Mark A. Wicks"
  257. static void create_docinfo (void)
  258. {
  259.   /* Create an empty Info entry and make it
  260.      be the root object */
  261.   if (debug) {
  262.     fprintf (stderr, "(create_docinfo)");
  263.   }
  264.   docinfo = pdf_new_dict ();
  265.   pdf_set_info (docinfo);
  266.   return;
  267. }
  268.  
  269. static void finish_docinfo(void)
  270. {
  271.   char *time_string, *banner;
  272.   banner = NEW (strlen(BANNER)+20,char);
  273.   sprintf(banner, BANNER, VERSION);
  274.   pdf_add_dict (docinfo, 
  275.         pdf_new_name ("Producer"),
  276.         pdf_new_string (banner, strlen (banner)));
  277.   RELEASE (banner);
  278.   time_string = asn_date();
  279.   pdf_add_dict (docinfo, 
  280.         pdf_new_name ("CreationDate"),
  281.         pdf_new_string (time_string, strlen (time_string)));
  282.   pdf_release_obj (docinfo);
  283.   return;
  284. }
  285.  
  286. void pdf_doc_merge_with_docinfo (pdf_obj *dictionary)
  287. {
  288.   pdf_merge_dict (docinfo, dictionary);
  289. }
  290.  
  291. void pdf_doc_merge_with_catalog (pdf_obj *dictionary)
  292. {
  293.   pdf_merge_dict (catalog, dictionary);
  294. }
  295.  
  296. static void create_catalog (void)
  297. {
  298.   if (debug) {
  299.     fprintf (stderr, "(create_catalog)");
  300.   }
  301.   catalog = pdf_new_dict ();
  302.   pdf_set_root (catalog);
  303.   /* Create /Type attribute */
  304.   pdf_add_dict (catalog,
  305.         pdf_link_obj (type_name),
  306.         pdf_new_name("Catalog"));
  307.  /* Create only those parts of the page tree required for the catalog.
  308.     That way, the rest of the page tree can be finished at any time */
  309.   start_page_tree(); 
  310.   /* Likewise for outline tree */
  311.   start_outline_tree ();
  312.   start_name_tree();
  313.   start_articles();
  314.   return;
  315. }
  316.  
  317. static void start_current_page_resources (void)
  318. {
  319.   /* work on resources to put in Pages */
  320.   if (debug) {
  321.     fprintf (stderr, "(start_current_page_resources)");
  322.   }
  323.   current_page_resources = pdf_new_dict ();
  324.   tmp1 = pdf_new_array ();
  325.   pdf_add_array (tmp1, pdf_new_name ("PDF"));
  326.   pdf_add_array (tmp1, pdf_new_name ("Text"));
  327.   pdf_add_array (tmp1, pdf_new_name ("ImageC"));
  328.   pdf_add_dict (current_page_resources,
  329.         pdf_new_name ("ProcSet"),
  330.         tmp1);
  331.   return;
  332. }
  333.  
  334. void pdf_doc_add_to_page_fonts (const char *name, pdf_obj
  335.                    *resource)
  336. {
  337. #ifdef MEM_DEBUG
  338.   MEM_START;
  339. #endif
  340.  
  341.   if (debug) {
  342.     fprintf (stderr, "(pdf_doc_add_to_page_fonts)");
  343.   }
  344.   if (this_page_fonts == NULL)
  345.     this_page_fonts = pdf_new_dict();
  346.   pdf_add_dict (this_page_fonts,
  347.         pdf_new_name (name), resource);
  348. #ifdef MEM_DEBUG
  349. MEM_END
  350. #endif
  351. }
  352.  
  353. void pdf_doc_add_to_page_xobjects (const char *name, pdf_obj
  354.                    *resource)
  355. {
  356.   if (debug) {
  357.     fprintf (stderr, "(pdf_doc_add_to_page_xojects)");
  358.   }
  359.   if (this_page_xobjects == NULL)
  360.     this_page_xobjects = pdf_new_dict ();
  361.   pdf_add_dict (this_page_xobjects,
  362.         pdf_new_name (name), 
  363.         resource);
  364. }
  365.  
  366.  
  367. void pdf_doc_add_to_page_resources (const char *name, pdf_obj *resource)
  368. {
  369.   if (debug) {
  370.     fprintf (stderr, "(pdf_doc_add_to_page_resources)");
  371.   }
  372.   pdf_add_dict (current_page_resources,
  373.         pdf_new_name (name), 
  374.         resource);
  375. }
  376.  
  377. void pdf_doc_add_to_page_annots (pdf_obj *annot)
  378. {
  379.   if (debug) {
  380.     fprintf (stderr, "(pdf_doc_add_to_page_annots)");
  381.   }
  382.   if (this_page_annots == NULL)
  383.     this_page_annots = pdf_new_array ();
  384.   pdf_add_array (this_page_annots,
  385.          annot);
  386. }
  387.  
  388. static pdf_obj *page_subtree (struct pages *pages, unsigned long npages,
  389.                pdf_obj *parent_ref)
  390. {
  391. #define PAGE_CLUSTER 4
  392.   pdf_obj *self, *self_ref, *kid_array;
  393.   self = pdf_new_dict();
  394.   /* This is a slight kludge which allow the subtree
  395.      dictionary generated by this routine to be merged with the
  396.      real page_tree dictionary, while keeping the indirect 
  397.      object references right */
  398.   if (parent_ref == NULL)
  399.     self_ref = pdf_ref_obj (page_tree);
  400.   else
  401.     self_ref = pdf_ref_obj (self);
  402.   pdf_add_dict (self, pdf_link_obj (type_name),
  403.         pdf_link_obj (pages_name));
  404.   pdf_add_dict (self, pdf_link_obj (count_name),
  405.         pdf_new_number((double) npages));
  406.   kid_array = pdf_new_array();
  407.   pdf_add_dict (self, pdf_link_obj (kids_name), 
  408.         kid_array);
  409.   if (parent_ref != NULL) {
  410.     pdf_add_dict (self, pdf_link_obj(parent_name),
  411.           parent_ref);
  412.   }
  413.   if (npages > 0 && npages <= PAGE_CLUSTER) {
  414.     int i;
  415.     for (i=0; i<npages; i++) {
  416.       pdf_add_array (kid_array, pdf_link_obj(pages[i].page_ref));
  417.       pdf_add_dict (pages[i].page_dict, pdf_link_obj (parent_name),
  418.             pdf_link_obj(self_ref));
  419.       pdf_release_obj (pages[i].page_dict);
  420.       pdf_release_obj (pages[i].page_ref);
  421.       pages[i].page_dict = NULL;
  422.       pages[i].page_ref = NULL;
  423.     }
  424.   } else if (npages > 0) {
  425.     int i;
  426.     for (i=0; i<PAGE_CLUSTER; i++) {
  427.       pdf_obj *subtree;
  428.       unsigned long start, end;
  429.       start = (i*npages)/PAGE_CLUSTER;
  430.       end = ((i+1)*npages)/PAGE_CLUSTER;
  431.       if (end-start>1) {
  432.     subtree = page_subtree (pages+start, end-start, pdf_link_obj(self_ref));
  433.     pdf_add_array (kid_array, pdf_ref_obj (subtree));
  434.     pdf_release_obj (subtree);
  435.       }
  436.       else {
  437.     pdf_add_array (kid_array, pdf_link_obj(pages[start].page_ref));
  438.     pdf_add_dict (pages[start].page_dict, pdf_link_obj(parent_name),
  439.               pdf_link_obj (self_ref));
  440.     pdf_release_obj (pages[start].page_dict);
  441.     pdf_release_obj (pages[start].page_ref);
  442.     pages[start].page_dict = NULL;
  443.     pages[start].page_ref = NULL;
  444.       }
  445.     }
  446.   }
  447.   pdf_release_obj (self_ref);
  448.   return self;
  449. }
  450.  
  451. static void finish_page_tree(void)
  452. {
  453.   pdf_obj *subtree;
  454.   if (debug) {
  455.     fprintf (stderr, "(finish_page_tree)");
  456.   }
  457.   
  458.   subtree = page_subtree (pages, page_count, NULL);
  459.   pdf_merge_dict (page_tree, subtree);
  460.   pdf_release_obj (subtree);
  461.   /* Generate media box at root of page tree and let the
  462.      other pages inherit it */
  463.   {
  464.     tmp1 = pdf_new_array ();
  465.     pdf_add_array (tmp1, pdf_new_number (0));
  466.     pdf_add_array (tmp1, pdf_new_number (0));
  467.     pdf_add_array (tmp1, pdf_new_number (ROUND(dev_page_width(),1.0)));
  468.     pdf_add_array (tmp1, pdf_new_number (ROUND(dev_page_height(),1.0)));
  469.     pdf_add_dict (page_tree, pdf_link_obj (mediabox_name), tmp1);
  470.   }
  471.   pdf_release_obj (page_tree);
  472.   pdf_add_dict (catalog,
  473.         pdf_link_obj (pages_name),
  474.         pdf_link_obj (page_tree_ref));
  475.   pdf_release_obj (page_tree_ref);
  476.   RELEASE (pages);
  477.   pdf_add_stream (glob_page_bop, "\n", 1);
  478.   pdf_release_obj (glob_page_bop);
  479.   pdf_add_stream (glob_page_eop, "\n", 1);
  480.   pdf_release_obj (glob_page_eop);
  481.   pdf_release_obj (coord_xform_stream);
  482.   pdf_release_obj (coord_xform_ref);
  483.   return;
  484. }
  485.  
  486. void pdf_doc_change_outline_depth(int new_depth)
  487. {
  488.   int i;
  489.   if (debug) {
  490.     fprintf (stderr, "(change_outline_depth)");
  491.   }
  492.   if (outline_depth >= MAX_OUTLINE_DEPTH -1)
  493.     ERROR ("Outline is too deep.");
  494.   if (new_depth == outline_depth)
  495.     /* Nothing to do */
  496.     return;
  497.   if (new_depth > outline_depth+1)
  498.     ERROR ("Can't increase outline depth by more than one at a time\n");
  499.   if (outline[outline_depth].entry == NULL)
  500.     ERROR ("change_outline_depth: Fix me, I'm broke. This shouldn't happen!");
  501.   /* Terminate all entries above this depth */
  502.   for (i=outline_depth-1; i>=new_depth; i--) {
  503.     pdf_add_dict (outline[i].entry,
  504.           pdf_new_name ("Last"),
  505.           pdf_ref_obj (outline[i+1].entry));
  506.     if (i > 0) 
  507.       tmp1 = pdf_new_number (-outline[i].kid_count);
  508.     else
  509.       tmp1 = pdf_new_number (outline[i].kid_count);
  510.  
  511.     pdf_add_dict (outline[i].entry,
  512.           pdf_link_obj (count_name),
  513.           tmp1);
  514.   }
  515.   /* Flush out all entries above this depth */
  516.   for (i=new_depth+1; i<=outline_depth; i++) {
  517.     pdf_release_obj (outline[i].entry);
  518.     outline[i].entry = NULL;
  519.     outline[i].kid_count = 0;
  520.   }
  521.   outline_depth = new_depth;
  522. }
  523.  
  524. static void finish_outline(void)
  525. {
  526.   if (debug)
  527.     fprintf (stderr, "(finish_outline)");
  528.   /* Link it into the catalog */
  529.   /* Point /Outline attribute to indirect reference */
  530.   pdf_doc_change_outline_depth (0);
  531.   pdf_add_dict (catalog,
  532.         pdf_new_name ("Outlines"),
  533.         pdf_ref_obj(outline[outline_depth].entry));
  534.   pdf_release_obj (outline[0].entry);
  535.   outline[0].entry = NULL;
  536. }
  537.  
  538.  
  539. void pdf_doc_add_outline (pdf_obj *dict)
  540. {
  541.   pdf_obj *new_entry;
  542.   if (outline_depth < 1)
  543.     ERROR ("Can't add to outline at depth < 1");
  544.   new_entry = pdf_new_dict ();
  545.   pdf_merge_dict (new_entry, dict);
  546.   /* Caller doesn't know we don't actually use the dictionary,
  547.      so he *gave* dict to us.  We have to free it */
  548.   pdf_release_obj (dict);
  549.   /* Tell it where its parent is */
  550.   pdf_add_dict (new_entry,
  551.         pdf_link_obj (parent_name),
  552.         pdf_ref_obj (outline[outline_depth-1].entry));
  553.   /* Give mom and dad the good news */
  554.   outline[outline_depth-1].kid_count += 1;
  555.  
  556.   /* Is this the first entry at this depth? */
  557.   if (outline[outline_depth].entry == NULL) {
  558.     /* Is so, tell the parent we are first born */
  559.     pdf_add_dict (outline[outline_depth-1].entry,
  560.           pdf_new_name ("First"),
  561.           pdf_ref_obj (new_entry));
  562.   }
  563.   else {
  564.     /* Point us back to sister */
  565.     pdf_add_dict (new_entry,
  566.           pdf_new_name ("Prev"),
  567.           pdf_ref_obj (outline[outline_depth].entry));
  568.     /* Point our elder sister toward us */
  569.     pdf_add_dict (outline[outline_depth].entry,
  570.           pdf_new_name ("Next"),
  571.           pdf_ref_obj (new_entry));
  572.     /* Bye-Bye sis */
  573.     pdf_release_obj (outline[outline_depth].entry);
  574.   }
  575.   outline[outline_depth].entry = new_entry;
  576.   /* Just born, so don't have any kids */
  577.   outline[outline_depth].kid_count = 0;
  578. }
  579.  
  580. struct dests 
  581. {
  582.   char *name;
  583.   unsigned length;
  584.   pdf_obj *array;
  585. };
  586. typedef struct dests dest_entry;
  587. static dest_entry *dests = NULL;
  588. unsigned long max_dests = 0;
  589.  
  590. static int CDECL cmp_dest (const void *d1, const void *d2)
  591. {
  592.   unsigned length;
  593.   int tmp;
  594.   length = MIN (((dest_entry *) d1) -> length, ((dest_entry *) d2) ->
  595.         length);
  596.   if ((tmp = memcmp (((dest_entry *) d1) -> name, ((dest_entry *) d2)
  597.               -> name, length)) != 0)
  598.     return tmp;
  599.   if (((dest_entry *) d1) -> length == ((dest_entry *) d2) -> length)
  600.     return 0;
  601.   return (((dest_entry *) d1) -> length < ((dest_entry *) d2) -> length ? -1 : 1 );
  602. }
  603.  
  604. static pdf_obj *name_subtree (dest_entry *dests, unsigned long ndests)
  605. {
  606. #define CLUSTER 4
  607.   pdf_obj *result, *name_array, *limit_array, *kid_array;
  608.   result = pdf_new_dict();
  609.   limit_array = pdf_new_array();
  610.   pdf_add_dict (result, pdf_link_obj(limits_name), limit_array);
  611.   pdf_add_array (limit_array, pdf_new_string(dests[0].name,
  612.                          dests[0].length)); 
  613.   pdf_add_array (limit_array, pdf_new_string(dests[ndests-1].name,
  614.                          dests[ndests-1].length));
  615.   if (ndests > 0 && ndests <= CLUSTER) {
  616.     int i;
  617.     name_array = pdf_new_array();
  618.     pdf_add_dict (result, pdf_new_name ("Names"),
  619.           name_array);
  620.     for (i=0; i<ndests; i++) {
  621.       pdf_add_array (name_array, pdf_new_string (dests[i].name,
  622.                          dests[i].length));
  623.       RELEASE (dests[i].name);
  624.       pdf_add_array (name_array, dests[i].array);
  625.     }
  626.   } else if (ndests > 0) {
  627.     int i;
  628.     kid_array = pdf_new_array();
  629.     pdf_add_dict (result, pdf_link_obj (kids_name), kid_array);
  630.     for (i=0; i<CLUSTER; i++) {
  631.       pdf_obj *subtree;
  632.       unsigned long start, end;
  633.       start = (i*ndests)/CLUSTER;
  634.       end = ((i+1)*ndests)/CLUSTER;
  635.       subtree = name_subtree (dests+start, end-start);
  636.       pdf_add_array (kid_array, pdf_ref_obj (subtree));
  637.       pdf_release_obj (subtree);
  638.     }
  639.   }
  640.   return result;
  641. }
  642.  
  643. static unsigned long number_dests = 0;
  644.  
  645. static void finish_dests_tree (void)
  646. {
  647.   pdf_obj *kid;
  648.   if (number_dests > 0) {
  649.     /* Sort before writing any /Dests entries */
  650.     qsort(dests, number_dests, sizeof(dests[0]), cmp_dest);
  651.     kid = name_subtree (dests, number_dests);
  652.     /* Each entry in dests has been assigned to another object, so
  653.        we can free the entire array without freeing the entries. */
  654.     RELEASE (dests);
  655.     pdf_add_dict (names_dict,
  656.           pdf_new_name ("Dests"),
  657.           pdf_ref_obj (kid));
  658.     pdf_release_obj (kid);
  659.   }
  660. }
  661.  
  662. void pdf_doc_add_dest (char *name, unsigned length, pdf_obj *array_ref)
  663. {
  664. #ifdef MEM_DEBUG
  665. MEM_START
  666. #endif
  667.   if (number_dests >= max_dests) {
  668.     max_dests += DESTS_ALLOC_SIZE;
  669.     dests = RENEW (dests, max_dests, dest_entry);
  670.   }
  671.   dests[number_dests].name = NEW (length, char);
  672.   memcpy (dests[number_dests].name, name, length);
  673.   dests[number_dests].length = length;
  674.   dests[number_dests].array = array_ref;
  675.   number_dests++;
  676. #ifdef MEM_DEBUG
  677. MEM_END
  678. #endif
  679.   return;
  680. }
  681.  
  682. struct articles
  683. {
  684.   char *name;
  685.   pdf_obj *info;
  686.   pdf_obj *first;
  687.   pdf_obj *last;
  688.   pdf_obj *this;
  689. };
  690.  
  691. typedef struct articles article_entry;
  692. static article_entry articles[MAX_ARTICLES];
  693. static unsigned long number_articles = 0;
  694.  
  695. static pdf_obj *articles_array;
  696. static void start_articles (void)
  697. {
  698.   articles_array = pdf_new_array();
  699. }
  700.  
  701. void pdf_doc_start_article (char *name, pdf_obj *info)
  702. {
  703.   if (number_articles >= MAX_ARTICLES) {
  704.     ERROR ("pdf_doc_add_article:  Too many articles\n");
  705.   }
  706.   if (name == NULL || strlen (name) == 0)
  707.     ERROR ("pdf_doc_start_article called null name");
  708.   articles[number_articles].name = NEW (strlen(name)+1, char);
  709.   strcpy (articles[number_articles].name, name);
  710.   articles[number_articles].info = info;
  711.   articles[number_articles].first = NULL;
  712.   articles[number_articles].last = NULL;
  713.   /* Start dictionary for this article even though we can't finish it
  714.      until we get the first bead */
  715.   articles[number_articles].this = pdf_new_dict();
  716.   number_articles++;
  717.   return;
  718. }
  719.  
  720. void pdf_doc_add_bead (char *article_name, pdf_obj *partial_dict)
  721. {
  722.   /* partial_dict should have P (Page) and R (Rect) already filled in */
  723.   /* See if the specified article exists */
  724.   int i;
  725.   for (i=0; i<number_articles; i++) {
  726.     if (!strcmp (articles[i].name, article_name))
  727.       break;
  728.   }
  729.   if (i == number_articles) {
  730.     fprintf (stderr, "Bead specified thread that doesn't exist\n");
  731.     return;
  732.   }
  733.   /* Is this the first bead? */
  734.   if (articles[i].last == NULL) {
  735.     articles[i].first = pdf_link_obj (partial_dict);
  736.     /* Add pointer to its first object */ 
  737.     pdf_add_dict (articles[i].this,
  738.           pdf_new_name ("F"),
  739.           pdf_ref_obj (articles[i].first));
  740.     /* Next add pointer to its Info dictionary */
  741.     pdf_add_dict (articles[i].this,
  742.           pdf_new_name ("I"),
  743.           pdf_ref_obj (articles[i].info));
  744.     /* Point first bead to parent article */
  745.     pdf_add_dict (partial_dict,
  746.           pdf_new_name ("T"),
  747.           pdf_ref_obj (articles[i].this));
  748.     /* Ship it out and forget it */
  749.     pdf_add_array (articles_array, pdf_ref_obj (articles[i].this));
  750.     pdf_release_obj (articles[i].this);
  751.     articles[i].this = NULL;
  752.   } else {
  753.     /* Link it in... */
  754.     /* Point last object to this one */
  755.     pdf_add_dict (articles[i].last,
  756.           pdf_new_name ("N"),
  757.           pdf_ref_obj (partial_dict));
  758.     /* Point this one to last */
  759.     pdf_add_dict (partial_dict,
  760.           pdf_new_name ("V"),
  761.           pdf_ref_obj (articles[i].last));
  762.     pdf_release_obj (articles[i].last);
  763.   }
  764.   articles[i].last = partial_dict;
  765.   if (this_page_beads == NULL)
  766.     this_page_beads = pdf_new_array();
  767.   pdf_add_array (this_page_beads,
  768.          pdf_ref_obj (partial_dict));
  769. }
  770.  
  771. void finish_articles(void)
  772. {
  773.   int i;
  774.   pdf_add_dict (catalog,
  775.         pdf_new_name ("Threads"),
  776.         pdf_ref_obj (articles_array));
  777.   pdf_release_obj (articles_array);
  778.   for (i=0; i<number_articles; i++) {
  779.     if (articles[i].last == NULL) {
  780.       fprintf (stderr, "Article started, but no beads\n");
  781.       break;
  782.     }
  783.     /* Close the loop */
  784.     pdf_add_dict (articles[i].last,
  785.           pdf_new_name ("N"),
  786.           pdf_ref_obj (articles[i].first));
  787.     pdf_add_dict (articles[i].first,
  788.           pdf_new_name ("V"),
  789.           pdf_ref_obj (articles[i].last));
  790.     pdf_release_obj (articles[i].first);
  791.     pdf_release_obj (articles[i].last);
  792.     pdf_release_obj (articles[i].info);
  793.     RELEASE (articles[i].name);
  794.   }
  795. }
  796.  
  797. #ifdef HAVE_LIBPNG
  798. static char thumbnail_opt = 0;
  799. static char *thumb_basename = NULL;
  800.  
  801. void pdf_doc_enable_thumbnails(void)
  802. {
  803.   thumbnail_opt = 1;
  804. }
  805.  
  806. #endif
  807.  
  808. void pdf_doc_finish_page ()
  809. {
  810. #ifdef MEM_DEBUG
  811. MEM_START
  812. #endif  
  813.   if (debug) {
  814.     fprintf (stderr, "(pdf_doc_finish_page)");
  815.   }
  816.   finish_pending_xobjects();
  817.   /* Flush this page */
  818.   /* Page_count is the index of the current page, starting at 1 */
  819.   tmp1 = pdf_new_array ();
  820.   pdf_add_array (tmp1, pdf_ref_obj (glob_page_bop));
  821.   if (this_page_bop) {
  822.     pdf_add_array (tmp1, pdf_ref_obj (this_page_bop));
  823.   }
  824.   pdf_add_array (tmp1, pdf_link_obj (coord_xform_ref));
  825.   pdf_add_array (tmp1, pdf_ref_obj (this_page_contents));
  826.   pdf_add_array (tmp1, pdf_ref_obj (glob_page_eop));
  827.   pdf_add_dict (pages[page_count].page_dict,
  828.         pdf_link_obj(contents_name), tmp1);
  829.   /* We keep .page_dict open because we don't know the parent yet */
  830.   if (this_page_bop != NULL) {
  831.     pdf_add_stream (this_page_bop, "\n", 1);
  832.     pdf_release_obj (this_page_bop);
  833.     this_page_bop = NULL;
  834.   }
  835.   if (this_page_contents != NULL) {
  836.     pdf_add_stream (this_page_contents, "\n", 1);
  837.     pdf_release_obj (this_page_contents);
  838.     this_page_contents = NULL;
  839.   }
  840.   if (this_page_annots != NULL) {
  841.     pdf_add_dict (pages[page_count].page_dict,
  842.           pdf_link_obj(annots_name),
  843.           pdf_ref_obj (this_page_annots));
  844.     pdf_release_obj (this_page_annots);
  845.     this_page_annots = NULL;
  846.   }
  847.   if (this_page_beads != NULL) {
  848.     pdf_add_dict (pages[page_count].page_dict,
  849.           pdf_link_obj (bead_name),
  850.           pdf_ref_obj (this_page_beads));
  851.     pdf_release_obj (this_page_beads);
  852.     this_page_beads = NULL;
  853.   }
  854.   if (this_page_fonts != NULL) {
  855.     pdf_add_dict (current_page_resources, 
  856.           pdf_new_name ("Font"),
  857.           pdf_ref_obj (this_page_fonts));
  858.     pdf_release_obj (this_page_fonts);
  859.     this_page_fonts = NULL;
  860.   }
  861.   if (this_page_xobjects != NULL) {
  862.     pdf_add_dict (current_page_resources,
  863.           pdf_new_name ("XObject"),
  864.           pdf_ref_obj (this_page_xobjects));
  865.     pdf_release_obj (this_page_xobjects);
  866.     this_page_xobjects = NULL;
  867.   }
  868.   if (current_page_resources != NULL) {
  869.     pdf_release_obj (current_page_resources);
  870.     current_page_resources = NULL;
  871.   }
  872. #ifdef HAVE_LIBPNG
  873.   if (thumbnail_opt) {
  874.     char *thumb_filename;
  875.     pdf_obj *thumbnail;
  876.     thumb_filename = NEW (strlen(thumb_basename)+7, char);
  877.     sprintf (thumb_filename, "%s.%ld", thumb_basename,
  878.          page_count%99999+1L);
  879.     thumbnail = do_thumbnail (thumb_filename);
  880.     RELEASE (thumb_filename);
  881.     if (thumbnail) 
  882.       pdf_add_dict (pages[page_count].page_dict,
  883.             pdf_link_obj (thumb_name),
  884.             thumbnail);
  885.   }
  886. #endif
  887.   page_count += 1;
  888. #ifdef MEM_DEBUG
  889.   MEM_END;
  890. #endif  
  891. }
  892.  
  893. pdf_obj *pdf_doc_current_page_resources (void)
  894. {
  895.   return current_page_resources;
  896. }
  897.  
  898.  
  899. static int highest_page_ref = 0;
  900. pdf_obj *pdf_doc_ref_page (unsigned long page_no)
  901. {
  902.   if (debug)
  903.     fprintf (stderr, "(doc_ref_page:page_no=%ld)", page_no);
  904.   if (page_no >= max_pages) {
  905.     resize_pages (page_no+PAGES_ALLOC_SIZE);
  906.   }
  907.   /* Has this page been referenced yet? */ 
  908.   if (pages[page_no-1].page_dict == NULL) {
  909.     /* If not, create it */
  910.     pages[page_no-1].page_dict = pdf_new_dict ();
  911.     /* and reference it */
  912.     pages[page_no-1].page_ref = pdf_ref_obj (pages[page_no-1].page_dict);
  913.   }
  914.   if (page_no > highest_page_ref)
  915.     highest_page_ref = page_no;
  916.   return pdf_link_obj (pages[page_no-1].page_ref);
  917. }
  918.  
  919. pdf_obj *pdf_doc_names (void)
  920. {
  921.   return names_dict;
  922. }
  923.  
  924. pdf_obj *pdf_doc_page_tree (void)
  925. {
  926.   return page_tree;
  927. }
  928.  
  929. pdf_obj *pdf_doc_catalog (void)
  930. {
  931.   return catalog;
  932. }
  933.  
  934. pdf_obj *pdf_doc_this_page (void)
  935. {
  936.   return pages[page_count].page_dict;
  937. }
  938.  
  939. pdf_obj *pdf_doc_this_page_ref (void)
  940. {
  941.   return pdf_doc_ref_page(page_count+1);
  942. }
  943.  
  944. pdf_obj *pdf_doc_prev_page_ref (void)
  945. {
  946.   if (page_count < 1) {
  947.     ERROR ("Reference to previous page, but no pages have been completed yet");
  948.   }
  949.   return pdf_doc_ref_page(page_count>0?page_count:1);
  950. }
  951.  
  952. pdf_obj *pdf_doc_next_page_ref (void)
  953. {
  954.   return pdf_doc_ref_page(page_count+2);
  955. }
  956.  
  957. void pdf_doc_new_page (void)
  958. {
  959. #ifdef MEM_DEBUG
  960. MEM_START
  961. #endif
  962.   if (debug) {
  963.     fprintf (stderr, "(pdf_doc_new_page)");
  964.     fprintf (stderr, "page_count=%ld, max_pages=%ld\n", page_count,
  965.          max_pages);
  966.   }
  967.   /* See if we need more pages allocated yet */
  968.   if (page_count >= max_pages) {
  969.     resize_pages(max_pages+PAGES_ALLOC_SIZE);
  970.   }
  971.   /* Was this page already instantiated by a forward reference to it? */
  972.   if (pages[page_count].page_ref == NULL) {
  973.     /* If not, create it. */
  974.     pages[page_count].page_dict = pdf_new_dict ();
  975.     /* and reference it */
  976.     pages[page_count].page_ref = pdf_ref_obj(pages[page_count].page_dict);
  977.   }
  978.   pdf_add_dict (pages[page_count].page_dict,
  979.         pdf_link_obj(type_name), pdf_link_obj(page_name));
  980.   /* start the contents stream for the new page */
  981.   this_page_contents = pdf_new_stream(STREAM_COMPRESS);
  982.   start_current_page_resources();
  983.   pdf_add_dict (pages[page_count].page_dict,
  984.         pdf_link_obj (resources_name),
  985.         pdf_ref_obj (current_page_resources));
  986.   /* Contents are still available as this_page_contents until next
  987.      page is started */
  988.   /* Even though the page is gone, a Reference to this page is kept
  989.      until program ends */
  990. #ifdef MEM_DEBUG
  991. MEM_END
  992. #endif
  993. }
  994.  
  995. void pdf_doc_add_to_page (char *buffer, unsigned length)
  996. {
  997.   pdf_add_stream (this_page_contents, buffer, length);
  998. }
  999.  
  1000. void pdf_doc_init (char *filename) 
  1001. {
  1002. #ifdef MEM_DEBUG
  1003.   MEM_START
  1004. #endif
  1005.   if (debug) fprintf (stderr, "pdf_doc_init:\n");
  1006.   pdf_out_init (filename);
  1007. #ifdef HAVE_LIBPNG
  1008.   /* Create a default name for thumbnail image files */
  1009.   if (thumbnail_opt) {
  1010.     if (strlen(filename)>4 && !strncmp (".pdf", filename+strlen(filename)-4,4)) {
  1011.       thumb_basename = NEW (strlen(filename)+1-4, char);
  1012.       strncpy (thumb_basename, filename, strlen(filename)-4);
  1013.       thumb_basename[strlen(filename)-4] = 0;
  1014.     } else {
  1015.       thumb_basename = NEW (strlen(filename)+1, char);
  1016.       strcpy (thumb_basename, filename);
  1017.     }
  1018.   }
  1019. #endif /* HAVE_LIBPNG */
  1020.   make_short_cuts();
  1021.   create_docinfo ();
  1022.   create_catalog ();
  1023. #ifdef MEM_DEBUG
  1024.   MEM_END
  1025. #endif
  1026. }
  1027.  
  1028. void pdf_doc_creator (char *s)
  1029. {
  1030.   pdf_add_dict (docinfo, pdf_new_name ("Creator"),
  1031.         pdf_new_string (s, strlen(s)));
  1032. }
  1033.  
  1034. void pdf_doc_close ()
  1035. {
  1036.   if (debug) fprintf (stderr, "pdf_doc_finish:\n");
  1037. #ifdef HAVE_LIBPNG
  1038.   if (thumb_basename)
  1039.     RELEASE (thumb_basename);
  1040. #endif /* HAVE_LIBPNG */
  1041.   /* Following things were kept around so user can add dictionary
  1042.      items */
  1043.   finish_docinfo();
  1044.   finish_page_tree();
  1045.   /* Add names dict to catalog */
  1046.   finish_outline();
  1047.   finish_dests_tree();
  1048.   finish_articles();
  1049.   pdf_add_dict (catalog,
  1050.         pdf_new_name ("Names"),
  1051.         pdf_ref_obj (names_dict));
  1052.   pdf_release_obj (names_dict);
  1053.   pdf_release_obj (catalog);
  1054.   /* Do consistency check on forward references to pages */
  1055.   if (highest_page_ref > page_count) {
  1056.     unsigned long i;
  1057.     fprintf (stderr, "\nWarning:  Nonexistent page(s) referenced\n");
  1058.     fprintf (stderr, "          (PDF file may not work right)\n");
  1059.     for (i=page_count; i<highest_page_ref; i++) {
  1060.       if (pages[i].page_dict) {
  1061.     pdf_release_obj (pages[i].page_dict);
  1062.     pdf_release_obj (pages[i].page_ref);
  1063.       }
  1064.     }
  1065.   }
  1066.   pdf_finish_specials();
  1067.   release_short_cuts();
  1068.   pdf_out_flush ();
  1069. }
  1070.  
  1071. static pdf_obj *build_scale_array (double a, double b, double c,
  1072.                    double d, double e, double f)
  1073. {
  1074.   pdf_obj *result;
  1075.   result = pdf_new_array();
  1076.   pdf_add_array (result, pdf_new_number (a));
  1077.   pdf_add_array (result, pdf_new_number (b));
  1078.   pdf_add_array (result, pdf_new_number (c));
  1079.   pdf_add_array (result, pdf_new_number (d));
  1080.   pdf_add_array (result, pdf_new_number (ROUND(e,0.01)));
  1081.   pdf_add_array (result, pdf_new_number (ROUND(f,0.01)));
  1082.   return result;
  1083. }
  1084.  
  1085. /* All this routine does is give the form a name
  1086.    and add a unity scaling matrix. It fills
  1087.    in required fields.  The caller must initialize
  1088.    the stream */
  1089.  
  1090.  
  1091. void doc_make_form_xobj (pdf_obj *this_form_contents, pdf_obj *bbox,
  1092.              double refptx, double refpty,
  1093.              double xscale, double yscale,
  1094.              pdf_obj *resources, char *form_name)
  1095. {
  1096.   pdf_obj *xobj_dict, *tmp1;
  1097.   xobj_dict = pdf_stream_dict (this_form_contents);
  1098.   
  1099.   pdf_add_dict (xobj_dict, pdf_new_name ("Name"), pdf_new_name(form_name));
  1100.   pdf_add_dict (xobj_dict, pdf_link_obj (type_name),
  1101.         pdf_new_name ("XObject"));
  1102.   pdf_add_dict (xobj_dict, pdf_new_name ("Subtype"),
  1103.         pdf_new_name ("Form"));
  1104.   pdf_add_dict (xobj_dict, pdf_new_name ("BBox"), bbox);
  1105.   pdf_add_dict (xobj_dict, pdf_new_name ("FormType"), 
  1106.         pdf_new_number(1.0));
  1107.   /* The reference point of an Xobject is at the lower left corner
  1108.      of the bounding box.  Since we would like to have an arbitrary
  1109.      reference point, we use a transformation matrix, translating
  1110.      the reference point to (0,0) */
  1111.   tmp1 = build_scale_array (xscale, 0, 0, yscale, -xscale*refptx, -yscale*refpty);
  1112.   pdf_add_dict (xobj_dict, pdf_new_name ("Matrix"), tmp1);
  1113.   pdf_add_dict (xobj_dict, pdf_link_obj (resources_name), resources);
  1114.   return;
  1115. }
  1116.  
  1117. struct resource_stack 
  1118. {
  1119.   pdf_obj *save_page_contents, *save_page_fonts;
  1120.   pdf_obj *save_page_xobjects, *save_page_resources;
  1121.   int xform_depth;
  1122. } res_stack[4];
  1123.  
  1124. static int xobjects_pending = 0;
  1125.  
  1126. /* begin_form_xobj creates an xobject with its "origin" at
  1127.    xpos and ypos that is clipped to the specified bbox. Note
  1128.    that the origin is not the lower left corner of the bbox */
  1129. pdf_obj *begin_form_xobj (double xpos, double ypos,
  1130.               double bbllx, double bblly,
  1131.               double bburx, double bbury, char *res_name)
  1132. {
  1133.   pdf_obj *bbox;
  1134.   if (xobjects_pending >= sizeof(res_stack)/sizeof(res_stack[0])) {
  1135.     fprintf (stderr, "\nForm XObjects nested too deeply.  Limit is %d\n",
  1136.          sizeof(res_stack)/sizeof(res_stack[0]));
  1137.     return NULL;
  1138.   }
  1139.   /* This is a real hack.  We basically treat each xobj as a separate mini
  1140.      page unto itself.  Save all the page structures and reinitialize
  1141.      them when we finish this xobject. */
  1142.   res_stack[xobjects_pending].save_page_resources = current_page_resources;
  1143.   current_page_resources = NULL;
  1144.   res_stack[xobjects_pending].save_page_xobjects = this_page_xobjects;
  1145.   this_page_xobjects = NULL;
  1146.   res_stack[xobjects_pending].save_page_fonts = this_page_fonts;
  1147.   this_page_fonts = NULL;
  1148.   res_stack[xobjects_pending].save_page_contents = this_page_contents;
  1149.   this_page_contents = NULL;
  1150.   res_stack[xobjects_pending].xform_depth = dev_xform_depth();
  1151.   xobjects_pending += 1;
  1152.   start_current_page_resources(); /* Starts current_page_resources */
  1153.   this_page_contents = pdf_new_stream (STREAM_COMPRESS);
  1154.   /* Make a bounding box for this Xobject */
  1155.   /* Translate coordinate system so reference point of object 
  1156.      is at 0 */
  1157.   bbox = pdf_new_array ();
  1158.   pdf_add_array (bbox, pdf_new_number (ROUND(bbllx,0.01)));
  1159.   pdf_add_array (bbox, pdf_new_number (ROUND(bblly,0.01)));
  1160.   pdf_add_array (bbox, pdf_new_number (ROUND(bburx,0.01)));
  1161.   pdf_add_array (bbox, pdf_new_number (ROUND(bbury,0.01)));
  1162.   /* Resource is already made, so call doc_make_form_xobj() */
  1163.   doc_make_form_xobj (this_page_contents, bbox,
  1164.               xpos, ypos, 1.0, 1.0,
  1165.               pdf_ref_obj(current_page_resources), res_name);
  1166.   /* Make sure the object is self-contained by adding the
  1167.      current font to the object stream */
  1168.   dev_reselect_font();
  1169.   /* Likewise for color */
  1170.   dev_do_color();
  1171.   return pdf_link_obj (this_page_contents);
  1172. }
  1173.  
  1174. void end_form_xobj (void)
  1175. {
  1176.   if (xobjects_pending>0) {
  1177.     xobjects_pending -= 1;
  1178.     dev_close_all_xforms(res_stack[xobjects_pending].xform_depth);
  1179.     if (this_page_xobjects) {
  1180.       pdf_add_dict (current_page_resources, pdf_new_name ("XObject"),
  1181.             pdf_ref_obj (this_page_xobjects));
  1182.       pdf_release_obj (this_page_xobjects);
  1183.     }
  1184.     if (this_page_fonts) {
  1185.       pdf_add_dict (current_page_resources, pdf_new_name ("Font"),
  1186.             pdf_ref_obj (this_page_fonts));
  1187.       pdf_release_obj (this_page_fonts);
  1188.     }
  1189.     if (current_page_resources)
  1190.       pdf_release_obj (current_page_resources);
  1191.     if (this_page_contents)
  1192.       pdf_release_obj (this_page_contents);
  1193.     current_page_resources = res_stack[xobjects_pending].save_page_resources;
  1194.     this_page_xobjects = res_stack[xobjects_pending].save_page_xobjects;
  1195.     this_page_fonts = res_stack[xobjects_pending].save_page_fonts;
  1196.     this_page_contents = res_stack[xobjects_pending].save_page_contents;
  1197.     /* Must reselect the font again in case there was a font change in
  1198.        the object */
  1199.     dev_reselect_font();
  1200.     /* Must reselect color too */
  1201.     dev_do_color();
  1202.   } else{
  1203.     fprintf (stderr, "\nSpecial: exobj: Tried to close a nonexistent xobject\n");
  1204.   }
  1205.   return;
  1206. }
  1207.  
  1208. void finish_pending_xobjects (void)
  1209. {
  1210.   if (xobjects_pending) {
  1211.     fprintf (stderr, "\nFinishing a pending form XObject at end of page\n"); 
  1212.     while (xobjects_pending--) {
  1213.       end_form_xobj();
  1214.     }
  1215.   }
  1216.   return;
  1217. }
  1218.  
  1219. static struct
  1220. {
  1221.   pdf_obj *annot_dict;
  1222.   unsigned char dirty;
  1223.   double llx, lly, urx, ury;
  1224. } breaking_state = {NULL, 0};
  1225.  
  1226. void pdf_doc_set_box (void)
  1227. {
  1228.   breaking_state.llx = dev_page_width();
  1229.   breaking_state.urx = 0;
  1230.   breaking_state.lly = dev_page_height();
  1231.   breaking_state.ury = 0;
  1232.   breaking_state.dirty = 0;
  1233.   return;
  1234. }
  1235.  
  1236. void pdf_doc_begin_annot (pdf_obj *dict)
  1237. {
  1238.   breaking_state.annot_dict = dict;
  1239.   pdf_doc_set_box ();
  1240.   dev_tag_depth ();
  1241.   return;
  1242. }
  1243.  
  1244. void pdf_doc_end_annot (void)
  1245. {
  1246.   pdf_doc_flush_annot();
  1247.   breaking_state.annot_dict = NULL;
  1248.   dev_untag_depth ();
  1249.   return;
  1250. }
  1251.  
  1252. void pdf_doc_flush_annot (void)
  1253. {
  1254.   pdf_obj *rectangle, *new_dict;
  1255.   double grow;
  1256.   grow = pdf_special_tell_grow ();
  1257.   if (breaking_state.dirty) {
  1258.     rectangle = pdf_new_array ();
  1259.     pdf_add_array (rectangle, pdf_new_number(ROUND(breaking_state.llx-grow, 0.01)));
  1260.     pdf_add_array (rectangle, pdf_new_number(ROUND(breaking_state.lly-grow, 0.01)));
  1261.     pdf_add_array (rectangle, pdf_new_number(ROUND(breaking_state.urx+grow, 0.01)));
  1262.     pdf_add_array (rectangle, pdf_new_number(ROUND(breaking_state.ury+grow, 0.01)));
  1263.     new_dict = pdf_new_dict ();
  1264.     pdf_add_dict (new_dict, pdf_new_name ("Rect"),
  1265.           rectangle);
  1266.     pdf_merge_dict (new_dict, breaking_state.annot_dict);
  1267.     pdf_doc_add_to_page_annots (pdf_ref_obj (new_dict));
  1268.     pdf_release_obj (new_dict);
  1269.   }
  1270.   pdf_doc_set_box();
  1271.   return;
  1272. }
  1273.  
  1274. void pdf_doc_expand_box (double llx, double lly, double urx, double
  1275.              ury)
  1276. {
  1277.   breaking_state.llx = MIN (breaking_state.llx, llx);
  1278.   breaking_state.lly = MIN (breaking_state.lly, lly);
  1279.   breaking_state.urx = MAX (breaking_state.urx, urx);
  1280.   breaking_state.ury = MAX (breaking_state.ury, ury);
  1281.   breaking_state.dirty = 1;
  1282.   return;
  1283. }
  1284.